#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <cassert>
#include <cstring>

#define MAX 1024

using namespace std;

// a. 管道的4中情况
//    1. 正常情况，如果管道中没有数据了，读端必须等待，直到管道中有数据为止（写端写入数据）
//    2. 正常情况，如果管道被写满了，写端必须等待，直到管道有空间为止（读端读走数据）
//    3. 写端关闭，读端一直读取，读断会读到read的返回值为0，表示读到文件结尾
//    4. 读断关闭，写端一直写入，OS会直接杀掉写端进程，通过向目标进程发送SIGPIPE(13)信号，终止目标进程
// b. 管道的5种特性
//    1. 匿名管道，可以允许具有血缘关系的进程之间进行进程间通信(爷孙也可以)，常用与父子，仅限于此
//    2. 匿名管道，默认给读写端要提供同步机制
//    3. 匿名管道，面向字节流的
//    4. 管道的生命周期是随进程的（父子进程都退出，管道资源会自动被释放）
//    5. 管道是单项通信的，半双工通信的一种特殊情况

int main()
{
    // 第1步，创建管道
    // pipe成功返回0，失败返回-1
    int pipefd[2] = {0};
    int n = pipe(pipefd);
    assert(n == 0); // debug模式下存在，意料之中用assert, 意料之外用if判断
    (void)n;        // 防止编译器告警，不使用n可能会存在警告
    cout << "pipefd[0]: " << pipefd[0] << ", pipefd[1]: " << pipefd[1] << endl;

    // 第2步，创建子进程
    pid_t id = fork();
    if (id < 0)
    {
        perror("fork fail:");
        return 1;
    }

    // 子写，父读
    // 第3步，父子关闭不需要的fd，形成单向通信的管道
    if (0 == id)
    {
        // child
        // TODO
        close(pipefd[0]);
        // w - 只向管道写入，没有打印
        int cnt = 10;
        while (cnt)
        {
            char message[MAX];
            // 将显示器格式化显示的内容，按照指定的格式打印到缓冲区，可以指定长度
            snprintf(message, sizeof(message), "hello father, I am child, pid:%d, cnt:%d", getpid(), cnt);
            cnt--;
            write(pipefd[1], message, strlen(message));
            sleep(1);
        }

        exit(0);
    }
    // parent
    close(pipefd[1]);

    // r
    char buffer[MAX];
    while(true)
    {
        ssize_t n = read(pipefd[0], buffer, sizeof(buffer)-1);
        if(n > 0)
        {
            buffer[n] = '\0';
            cout << getpid() << "," << "child say: " << buffer << " to me!" << endl;
        }
        else if(n == 0)
        {
            cout << "father process exit!" << endl;
            break;
        }
        else
        {
            perror("read");
            exit(1);
        }
    }

    pid_t rid = waitpid(id, nullptr, 0);
    if (rid == id)
    {
        cout << "wait success!" << endl;
    }

    return 0;
}
